package com.xt.base.xml;

import java.lang.reflect.Array;
import java.lang.reflect.Field;
import java.util.Collection;
import java.util.List;
import java.util.Map;

import net.sf.json.JSONException;

import org.apache.commons.lang.StringUtils;
import org.dom4j.Document;
import org.dom4j.DocumentHelper;
import org.dom4j.Element;

import com.xt.base.reflection.BaseReflector;
import com.xt.base.reflection.Simple;
import com.xt.base.reflection.UV.Tuple;
import com.xt.base.util.json.JSONReflector;

public class XML {
	
	protected XML(){}
	
	@SuppressWarnings("unchecked")
	public static Object revival(Document doc){
		try{
			Element root = doc.getRootElement();
			String className = className(root);
			Class model = BaseReflector.getModel(className);
			Object res = BaseReflector.getInstance(model);
			List<Element> elements = root.elements();
			for(Element element:elements){
				String property = element.getName();
				Field field = BaseReflector.getFieldFromAssign(model, property);
				Object o = object(element,field.getType());
				BaseReflector.setValueIgnoreAccessable(res, field, o);
			}
			return res;
		}catch(Exception e){
			throw new RuntimeException(e);
		}
	}
	
	@SuppressWarnings("unchecked")
	public static Tuple<Document, Element> document(Class clazz){
		Document doc = DocumentHelper.createDocument();
		Element ele = doc.addElement(clazz.getSimpleName());
		ele.addAttribute("nameSpace", clazz.getPackage().getName());
		return new Tuple<Document, Element>(doc,ele);
	}
	
	@SuppressWarnings("unchecked")
	private static Object object(Element ele,Class clazz){
		try{
			Object res = null;
			if(clazz.isArray()){
				Class comClass = clazz.getComponentType();
				List<Element> elements = ele.elements();
				Object[] oArray = (Object[])Array.newInstance(comClass, elements.size());
				for(int i=0;i<oArray.length;i++)oArray[i] = object(elements.get(i));
				res = oArray;
			}else if(BaseReflector.isCollection(clazz)){
				Collection<Object> coll = JSONReflector.newCollection(clazz);
				if(coll != null){
					List<Element> elements = ele.elements();
					for(Element e:elements)coll.add(object(e));
					res = coll;
				}else throw new JSONException("a group value isnot a Array or Collection.");
			}else if(!Simple.is(clazz) && String.class != clazz){
				System.out.println(clazz);
				String className = className(ele);
				Class model = BaseReflector.getModel(className);
				res = BaseReflector.getInstance(model);
				List<Element> elements = ele.elements();
				for(Element element:elements){
					String property = element.getName();
					Field field = BaseReflector.getFieldFromAssign(model, property);
					Object o = object(element,field.getType());
					BaseReflector.setValueIgnoreAccessable(res, field, o);
				}
			}else res = ele.getText();
			return res;
		}catch(Exception e){
			throw new RuntimeException(e);
		}
	}
	
	@SuppressWarnings("unchecked")
	private static Object object(Element ele){
		try{
			String className = className(ele);
			List<Element> elements = ele.elements();
			if(elements.isEmpty())return ele.getText();
			if(className != null){
				Class model = BaseReflector.getModel(className);
				Object res = BaseReflector.getInstance(model);
				for(Element element:elements){
					String property = element.getName();
					Field field = BaseReflector.getFieldFromAssign(model, property);
					Object o = object(element,field.getType());
					BaseReflector.setValueIgnoreAccessable(res, field, o);
				}
				return res;
			}
			Object[] oArray = new Object[elements.size()];
			for(int i=0;i<oArray.length;i++)oArray[i] = object(elements.get(i));
			return oArray;
		}catch(Exception e){
			throw new RuntimeException(e);
		}
	}
	
	@SuppressWarnings("unchecked")
	public static void add(Element ele,Map<String,Object> map){
		for(Map.Entry<String, Object> entity:map.entrySet()){
			Object o = entity.getValue();
			if(o instanceof Map){
				Map<String,Object> m = (Map<String,Object>)o;
				if(!m.isEmpty())add(ele.addElement(entity.getKey()),m);
			}else if(o instanceof Collection){
				Collection c = (Collection<Object>)o;
				if(!c.isEmpty())add(ele.addElement(entity.getKey()),c);
			}else if(o.getClass().isArray()){
				Object[] a = (Object[])o;
				if(a.length> 0)add(ele.addElement(entity.getKey()),a);
			}else ele.addElement(entity.getKey()).addText(String.valueOf(entity.getValue()));;
		}
	}
	
	public static String getText(Element element,String name){
		if(element == null || StringUtils.isEmpty(name))
			return null;
		String attrValue = element.attributeValue(name);
		if(!StringUtils.isEmpty(attrValue))
			return attrValue;
		Element nElement = element.element(name);
		if(nElement == null)
			return null;
		return nElement.getText();
		
	}
	
	public static String getSubText(Element element,String subName,String name){
		if(element == null || StringUtils.isEmpty(name) || StringUtils.isEmpty(subName))
			return null;
		Element subElement = element.element(subName);
		if(subElement == null)return null;
		return getText(subElement,name);
	}
	
	private static void add(Element ele,Collection<Object> coll){
		add(ele,coll.toArray());
	}
	
	@SuppressWarnings("unchecked")
	private static void add(Element ele,Object[] objs){
		for(Object o:objs){
			if(o instanceof Map){
				Map<String,Object> m = (Map<String,Object>)o;
				if(!m.isEmpty())add(element(ele,(String)m.get("clazz")),m);
			}else if(o instanceof Collection){
				Collection<Object> c = (Collection<Object>)o;
				if(!c.isEmpty())add(element(ele,c.iterator().next().getClass()),c);
			}else if(o.getClass().isArray()){
				Object[] x = (Object[])o;
				if(x.length > 0)add(element(ele,x.getClass().getComponentType()),x);
			}else element(ele,o.getClass()).addText(String.valueOf(o));
		}
	}
	
	@SuppressWarnings("unchecked")
	private static Element element(Element parent,Class clazz){
		if(clazz == null)clazz = Object.class;
		return element(parent,clazz.getName());
	}
	
	private static Element element(Element parent,String className){
		int l = className.lastIndexOf('.');
		return element(parent,className.substring(l+1),className.substring(0,l));
	}
	
	private static Element element(Element parent,String name,String namespace){
		return parent.addElement(name).addAttribute("nameSpace", namespace);
	}
	
	private static String className(Element ele){
		String res = ele.attributeValue("nameSpace");
		return res == null?ele.elementText("clazz"):res + "." + ele.getName();
	}
}
